﻿using System;
using System.Linq;
using PhoneNetTools.Utilities;

namespace PhoneNetTools.Dns.Records
{
    /// <summary>
    /// A generic representation of a resource record. This implementation is used for each resource record that is not explicitly supported by the library.
    /// </summary>
    public class GenericResourceRecord : ResourceRecordBase
    {
        /// <summary>
        /// Gets or sets the time interval, in seconds, that the resource record may be cached before the source of the information should again be consulted.
        /// Zero values are interpreted to mean that the RR can only be used for the transaction in progress, and should not be cached.
        /// </summary>
        public UInt32 Ttl
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the length of the data field.
        /// </summary>
        public UInt16 DataLength
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the raw data, if any, that describes the resource.
        /// The format of this information varies according to the type and class of the resource record.
        /// </summary>
        public byte[] Data
        {
            get;
            set;
        }

        /// <summary>
        /// Gets the length of the whole resource record, in bytes.
        /// </summary>
        public override int Length
        {
            get
            {
                // compute the string length
                // string length (assuming ASCII!) + one additional byte for the additional length prefix if there's at least one dot (e.g. 2 dots => 3 parts => 3 prefixes),
                // plus the 0-termination
                var dotCount = Name.Count(o => o == '.');
                var stringLength = Name.Length + (dotCount > 0 ? 1 : 0) + 1;

                // string length + Type + Class + Ttl + DataLength + [actual length of data]
                return stringLength + sizeof(UInt16) + sizeof(UInt16) + sizeof(UInt32) + sizeof(UInt16) + DataLength;
            }
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="GenericResourceRecord"/> class and sets the class to <c>IN</c>.
        /// </summary>
        public GenericResourceRecord()
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="GenericResourceRecord"/> class using the given type, and sets the class to <c>IN</c>.
        /// </summary>
        /// <param name="type">The type.</param>
        public GenericResourceRecord(QType type)
            : base(type)
        {
        }

        /// <summary>
        /// Converts the content of the resource record to a raw representation.
        /// Derived classes are supposed to override this, call the base
        /// implementation and then add their own custom data.
        /// </summary>
        /// <param name="rawData">The raw data byte array to write the content to.</param>
        /// <param name="startIndex">The start index to start writing at.</param>
        /// <returns>
        /// The new index that points to the byte directly after the last one written by this method.
        /// </returns>
        public override int ToRawData(byte[] rawData, int startIndex)
        {
            // let the base class serialize, then add our own data
            startIndex = base.ToRawData(rawData, startIndex);

            // Ttl
            var rawTtl = BigEndianBitConverter.GetBytes(Ttl);
            Array.Copy(rawTtl, 0, rawData, startIndex, rawTtl.Length);
            startIndex += rawTtl.Length;

            // data length
            var rawDataLength = BigEndianBitConverter.GetBytes(DataLength);
            Array.Copy(rawDataLength, 0, rawData, startIndex, rawDataLength.Length);
            startIndex += rawDataLength.Length;

            // data
            Array.Copy(Data, 0, rawData, startIndex, Data.Length);
            startIndex += Data.Length;

            return startIndex;
        }

        /// <summary>
        /// Configures the resource record from a raw representation.
        /// Derived classes are supposed to override this, call the base
        /// implementation and then extract their own custom data,
        /// starting at the index returned by the base class implementation.
        /// </summary>
        /// <param name="rawData">The raw data byte array to extract data from.</param>
        /// <param name="startIndex">The start index to start extracting data at.</param>
        /// <returns>
        /// The new index that points to the byte directly after the last one read by this method.
        /// </returns>
        public override int FromRawData(byte[] rawData, int startIndex)
        {
            // ttl
            Ttl = BigEndianBitConverter.ToUInt32(rawData, startIndex);
            startIndex += sizeof(UInt32);

            // DataLength
            DataLength = BigEndianBitConverter.ToUInt16(rawData, startIndex);
            startIndex += sizeof(UInt16);

            // data
            Data = new byte[DataLength];
            Array.Copy(rawData, startIndex, Data, 0, DataLength);
            startIndex += DataLength;

            return startIndex;
        }
    }
}
